import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * socketServer
 * xyz.socketserver.server
 * Created by Blue on 2021/11/9.
 */
@Slf4j
public class NIOServer {
	private final InetAddress address;
	private final int port;
	private Selector selector;
	
	private static final int BUFF_SIZE = 1024;
	
	public NIOServer(InetAddress address, int port) throws IOException {
		this.address = address;
		this.port = port;
		startServer();
	}
	
	private void startServer() throws IOException {
		// 获得selector及通道(socketChannel)
		this.selector = Selector.open();
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
		serverChannel.configureBlocking(false);
		
		// 绑定地址及端口
		InetSocketAddress listenAddress = new InetSocketAddress(this.address, this.port);
		serverChannel.socket().bind(listenAddress);
		serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
		
		
		while (true) {
			log.info("服务器等待新的连接和selector选择…");
			this.selector.select();
			// 选择key工作
			Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();
			while (keys.hasNext()) {
				SelectionKey key = keys.next();
				// 防止出现重复的key，处理完需及时移除
				keys.remove();
				//无效直接跳过
				if (!key.isValid()) {
					continue;
				}
				if (key.isAcceptable()) {
					this.accept(key);
				} else if (key.isReadable()) {
					this.read(key);
				} else if (key.isWritable()) {
					this.write(key);
				} else if (key.isConnectable()) {
					this.connect(key);
				}
			}
		}
	}
	
	private void connect(SelectionKey key) throws IOException {
		SocketChannel channel = (SocketChannel) key.channel();
		if (channel.finishConnect()) {
			// 成功
			log.info("成功连接了");
		} else {
			// 失败
			log.info("失败连接");
		}
	}
	
	private void accept(SelectionKey key) throws IOException {
		ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
		SocketChannel channel = serverChannel.accept();
		channel.configureBlocking(false);
		channel.register(this.selector, SelectionKey.OP_READ);
		Socket socket = channel.socket();
		SocketAddress remoteAddress = socket.getRemoteSocketAddress();
		log.info("连接到: " + remoteAddress);
	}
	
	private void read(SelectionKey key) throws IOException {
		SocketChannel channel = (SocketChannel) key.channel();
		
		ByteBuffer buffer = ByteBuffer.allocate(BUFF_SIZE);
		int numRead = channel.read(buffer);
		if (numRead == -1) {
			log.info("关闭客户端连接: " + channel.socket().getRemoteSocketAddress());
			channel.close();
			return;
		}
		String msg = new String(buffer.array()).trim();
		log.info("得到了: " + msg);
		
		// 回复客户端
		String reMsg = msg + " 你好，这是BIOServer给你的回复消息:" + System.currentTimeMillis();
		channel.write(ByteBuffer.wrap(reMsg.getBytes()));
	}
	
	private void write(SelectionKey key) throws IOException {
		ByteBuffer byteBuffer = ByteBuffer.allocate(BUFF_SIZE);
		byteBuffer.flip();
		SocketChannel clientChannel = (SocketChannel) key.channel();
		while (byteBuffer.hasRemaining()) {
			clientChannel.write(byteBuffer);
		}
		byteBuffer.compact();
	}
	
	public static void main(String[] args) throws IOException {
		new NIOServer(null, 10002);
	}
}
